package com.abo.yaas.view.widgets;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;

import org.vaadin.autoreplacefield.DoubleField;
import org.vaadin.hene.expandingtextarea.ExpandingTextArea;
import org.vaadin.henrik.refresher.Refresher;
import org.vaadin.henrik.refresher.Refresher.RefreshListener;

import com.abo.yaas.authentication.SessionHandler;
import com.abo.yaas.authentication.UserChangeEvent;
import com.abo.yaas.authentication.UserChangeListener;
import com.abo.yaas.domain.Auction;
import com.abo.yaas.domain.AuctionState;
import com.abo.yaas.domain.Bid;
import com.abo.yaas.domain.User;
import com.abo.yaas.i18n.Lang;
import com.abo.yaas.i18n.LocaleChangeListener;
import com.abo.yaas.i18n.LocaleChangedEvent;
import com.abo.yaas.utils.EmailUtils;
import com.abo.yaas.view.AbstractView;
import com.abo.yaas.view.DynamicImageResource;
import com.abo.yaas.webservice.ResourceProvider;
import com.abo.yaas.webservice.exceptions.CommunicationException;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.PropertyFormatter;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.terminal.StreamResource.StreamSource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.Embedded;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.HorizontalSplitPanel;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.VerticalSplitPanel;
import com.vaadin.ui.themes.ChameleonTheme;

@SuppressWarnings("serial")
public class AuctionView extends AbstractView<VerticalLayout> implements Button.ClickListener, UserChangeListener,
        LocaleChangeListener {

    private final Panel panel = new Panel();

    private final VerticalLayout bottomLayout = new VerticalLayout();

    private final Embedded image = new Embedded();

    private final Button addBidButton = new LocaleSensitiveButton("PLACE_BID_BUTTON", this, "placeBid");

    private final Button editButton = new Button("Edit", this, "startEditing");

    private final Button saveButton = new Button("Save", this, "saveEdited");

    private final DoubleField bidAmountField = new DoubleField();

    private final BidTable bidTable = new BidTable();

    private final Refresher mainRefresher = new Refresher();

    private BeanItem<Auction> item = null;

    private final StreamSource streamSource = new StreamSource() {
        @Override
        public InputStream getStream() {
            if (item == null) {
                return null;
            }

            final byte[] data = getAuction().getPreview();
            if (data == null) {
                return null;
            }
            return new ByteArrayInputStream(data);
        }
    };

    public AuctionView() {
        super(null);

        final VerticalLayout wrapper = new VerticalLayout();
        wrapper.addStyleName("auctionview");
        wrapper.setSizeFull();

        panel.setWidth("60%");
        panel.setHeight("75%");
        setupLayout();

        wrapper.addComponent(panel);
        wrapper.setComponentAlignment(panel, Alignment.MIDDLE_CENTER);
        wrapper.addComponent(mainRefresher);
        wrapper.setExpandRatio(panel, 1f);

        setContent(wrapper);

        mainRefresher.setRefreshInterval(50000);
        mainRefresher.addListener(new RefreshListener() {
            @Override
            public void refresh(Refresher source) {
                if (item == null) {
                    return;
                }
                final Auction newAuction = ResourceProvider.getAuctionProxy().getEntity(getAuction().getId());
                if (newAuction != null && !newAuction.equals(getAuction())) {
                    resetDataSource(newAuction);
                }
            }
        });
        SessionHandler.addListener(this);
        Lang.addListener(this);
    }

    @Override
    public void activated(Object... params) {
        for (final Object param : params) {
            if (param instanceof Auction) {
                final Auction a = (Auction) param;
                initializeViewWithDS(a);
                boolean isOwner = a.getOwner().equals(SessionHandler.getUser());
                editButton.setVisible(isOwner);
                saveButton.setVisible(isOwner);
                descriptionTextBox.setReadOnly(true);
                break;
            }
        }
    }

    public void banAuction() {
        final Auction a = getAuction();
        try {
            a.setState(AuctionState.AS_BANNED);
            ResourceProvider.getAuctionProxy().saveEntity(a);
            resetDataSource(a);
        } catch (final CommunicationException e) {
            final CommunicationExceptionNotification n = new CommunicationExceptionNotification("Banning failed");
            getWindow().showNotification(n);
        }
    }

    private Panel buildBidTablePanel() {

        final Panel bidTablePanel = new LocaleSensitivePanel("BID_PANEL");
        final VerticalLayout layout = (VerticalLayout) bidTablePanel.getContent();
        bidTablePanel.setSizeFull();
        bidTablePanel.addStyleName(ChameleonTheme.PANEL_LIGHT);

        layout.setSizeFull();
        final Refresher refresher = new Refresher() {
            {
                setRefreshInterval(5000);
                addListener(new Refresher.RefreshListener() {
                    @Override
                    public void refresh(Refresher source) {
                        bidTable.requestRepaint();
                    }
                });
            }
        };

        layout.setMargin(false);
        layout.addComponent(refresher);
        layout.addComponent(bidTable);
        layout.setExpandRatio(bidTable, 1f);
        return bidTablePanel;
    }

    private void buildBottomLayout() {
        bidAmountField.setCaption("Amount");
        bidAmountField.setImmediate(true);
        bidAmountField.setWriteThrough(true);
        bidAmountField.addListener(new TextChangeListener() {
            @Override
            public void textChange(TextChangeEvent event) {
                Double amount = 0.0;
                try {
                    amount = Double.valueOf(event.getText());
                } catch (final NumberFormatException e) {
                }
                final Double currentPrice = item.getBean().getCurrentPrice().doubleValue();
                if (amount < currentPrice) {
                    bidAmountField.setValue(currentPrice * 1.01);
                }
            }
        });

        bottomLayout.removeAllComponents();
        bottomLayout.setSizeUndefined();
        bottomLayout.setSpacing(true);

        if (SessionHandler.getUser() != null) {
            bottomLayout.addComponent(bidAmountField);
            bottomLayout.addComponent(addBidButton);
            if (SessionHandler.getUser().getIsAdmin()) {
                final Button banAuctionButton = new Button("BAN THIS AUCTION", this, "banAuction");
                bottomLayout.addComponent(banAuctionButton);
            }
        }
    }

    private final Label currentPriceLabel = new LocaleSensitiveLabel("CURRENT_PRICE_LABEL");

    private final Label creatorLabel = new LocaleSensitiveLabel("AUCTION_CREATED_BY_LABEL");

    private final Label stateLabel = new LocaleSensitiveLabel("AUCTION_STATE_LABEL");

    private final Label titleLabel = new LocaleSensitiveLabel("AUCTION_STATE_LABEL");

    private final Label winnerLabel = new LocaleSensitiveLabel("AUCTION_STATE_LABEL");

    private final Label minBidLabel = new LocaleSensitiveLabel("AUCTION_STATE_LABEL");

    private final Label endLabel = new LocaleSensitiveLabel("AUCTION_STATE_LABEL");

    private final ExpandingTextArea descriptionTextBox = new ExpandingTextArea(Lang.getMessage("DESCRIPTION"));

    private Panel buildDetailsPanel() {
        final Panel detailsPanel = new LocaleSensitivePanel("DETAILS_PANEL");
        detailsPanel.setSizeFull();
        detailsPanel.addStyleName(ChameleonTheme.PANEL_LIGHT);
        final VerticalLayout layout = (VerticalLayout) detailsPanel.getContent();
        layout.setSizeFull();
        layout.setSpacing(true);

        descriptionTextBox.setWidth("100%");
        descriptionTextBox.setReadOnly(true);

        layout.addComponent(titleLabel);
        layout.addComponent(stateLabel);
        layout.addComponent(creatorLabel);
        layout.addComponent(minBidLabel);
        layout.addComponent(currentPriceLabel);
        layout.addComponent(winnerLabel);
        layout.addComponent(descriptionTextBox);

        layout.addComponent(editButton);
        layout.addComponent(saveButton);

        final HorizontalLayout buttons = new HorizontalLayout();
        buttons.setHeight(null);
        buttons.setWidth("100%");
        buttons.addComponent(editButton);
        buttons.addComponent(saveButton);
        editButton.setWidth("100%");
        saveButton.setWidth("100%");

        layout.addComponent(buttons);
        layout.setExpandRatio(descriptionTextBox, 1f);
        return detailsPanel;
    }

    @SuppressWarnings("unchecked")
    private void resetDataSource(final Auction auction) {
        if (auction == null) {
            throw new IllegalArgumentException("New Datasource Must Not be Null!");
        }
        item = new BeanItem<Auction>(auction);

        panel.setCaption(getAuction().getTitle());

        stateLabel.setPropertyDataSource(new PropertyFormatter(item.getItemProperty("state")) {
            @Override
            public Object parse(String formattedValue) throws Exception {
                return formattedValue;
            }

            @Override
            public String format(Object value) {
                return value.toString();
            }
        });
        titleLabel.setPropertyDataSource(item.getItemProperty("title"));
        winnerLabel.setPropertyDataSource(item.getItemProperty("winnerUserName"));
        creatorLabel.setPropertyDataSource(item.getItemProperty("ownerLogin"));
        minBidLabel.setPropertyDataSource(item.getItemProperty("minimumBid"));
        endLabel.setPropertyDataSource(item.getItemProperty("currentPrice"));
        currentPriceLabel.setPropertyDataSource(item.getItemProperty("currentPrice"));
        descriptionTextBox.setPropertyDataSource(item.getItemProperty("description"));
    }

    @Override
    public void addComponent(Component c) {
        getContent().addComponent(c);
    }

    @Override
    public void buttonClick(ClickEvent event) {/* NOP */
    }

    @Override
    public void deactivated(Object... params) {/* NOP */
    }

    final Auction getAuction() {
        return item == null ? null : (Auction) item.getBean();
    }

    private void initializeViewWithDS(final Auction auction) {
        resetDataSource(auction);
        bidTable.updateAuction(getAuction());
        image.setSource(new DynamicImageResource(streamSource, getApplication()));
    }

    @Override
    public void localeChanged(LocaleChangedEvent event) {
        descriptionTextBox.setCaption(Lang.getMessage("DESCRIPTION"));
    }

    @Override
    public void onLogin(UserChangeEvent event) {
        updateViewOnUserChange();
    }

    @Override
    public void onLogout(UserChangeEvent event) {
        updateViewOnUserChange();
    }

    public void placeBid() {
        try {
            final Bid bid = new Bid(SessionHandler.getUser(), getAuction().getId());
            bid.setAmount(BigDecimal.valueOf(bidAmountField.getValue()));
            final ArrayList<User> users = new ArrayList<User>();
            users.add(getAuction().getOwner());
            users.add(bid.getUser());
            if (getAuction().getLeadingBid() != null) {
                users.add(getAuction().getLeadingBid().getUser());
            }
            ResourceProvider.getBidProxy().saveEntity(bid);
            EmailUtils.newBidPlacedEmail(users, bid, getAuction());
        } catch (final CommunicationException e) {
            getWindow().showNotification(new CommunicationExceptionNotification(e));
        }
    }

    public void saveEdited() {
        try {
            final Auction newBean = ResourceProvider.getAuctionProxy().saveEntity(getAuction());
            resetDataSource(newBean);
            descriptionTextBox.setReadOnly(true);
        } catch (final CommunicationException e) {
            final String newDesc = descriptionTextBox.getValue().toString();
            final Auction targetAuction = ResourceProvider.getAuctionProxy().getEntity(getAuction().getId());
            targetAuction.setDescription(newDesc);
            resetDataSource(targetAuction);
            saveEdited();
        }
    }

    private void setupLayout() {
        buildBottomLayout();

        final Panel detailsPanel = buildDetailsPanel();
        final Panel bidPanel = buildBidTablePanel();

        final HorizontalSplitPanel detailsSplit = new HorizontalSplitPanel();
        final VerticalLayout wrapper = new VerticalLayout();

        wrapper.setMargin(true);
        wrapper.setSizeFull();
        wrapper.addComponent(image);
        wrapper.addComponent(bottomLayout);

        detailsSplit.setFirstComponent(wrapper);
        detailsSplit.setSecondComponent(detailsPanel);

        wrapper.setComponentAlignment(bottomLayout, Alignment.MIDDLE_CENTER);
        wrapper.setComponentAlignment(image, Alignment.MIDDLE_CENTER);
        wrapper.setExpandRatio(image, 1f);

        mainRefresher.setSizeUndefined();

        final VerticalSplitPanel split = new VerticalSplitPanel();
        split.setFirstComponent(detailsSplit);
        split.setSecondComponent(bidPanel);
        split.setSplitPosition((int) bidPanel.getHeight(), UNITS_PIXELS, true);
        panel.setContent(split);

    }

    public void startEditing() {
        descriptionTextBox.setReadOnly(false);
    }

    private void updateViewOnUserChange() {
        buildBottomLayout();
    }
}
